package com.wmada.toolkit.rest;

import com.wmada.toolkit.common.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.entity.ContentType;
import org.jetbrains.annotations.NotNull;

import javax.servlet.http.HttpServletRequest;
import java.util.*;

/**
 * The type Mime type.
 *
 * @author will.ma
 * @date 2016/4/7
 */
@SuppressWarnings({"WeakerAccess", "unused"})
public class MimeType {
    private static final Map<String, Set<MimeType>> MIME_MAP = new LinkedHashMap<>();
    private static final Map<String, Set<MimeType>> TYPE_MAP = new LinkedHashMap<>();

    static {
        addMimeType("*", "application/octet-stream");
        addMimeType("001", "application/x-001");
        addMimeType("301", "application/x-301");
        addMimeType("323", "text/h323");
        addMimeType("906", "application/x-906");
        addMimeType("907", "drawing/907");
        addMimeType("a11", "application/x-a11");
        addMimeType("acp", "audio/x-mei-aac");
        addMimeType("ai", "application/postscript");
        addMimeType("aif", "audio/aiff");
        addMimeType("aifc", "audio/aiff");
        addMimeType("aiff", "audio/aiff");
        addMimeType("anv", "application/x-anv");
        addMimeType("apk", "application/vnd.android.package-archive");
        addMimeType("asa", "text/asa");
        addMimeType("asf", "video/x-ms-asf");
        addMimeType("asp", "text/asp");
        addMimeType("asx", "video/x-ms-asf");
        addMimeType("au", "audio/basic");
        addMimeType("avi", "video/avi");
        addMimeType("awf", "application/vnd.adobe.workflow");
        addMimeType("xml", "text/xml");
        addMimeType("biz", "text/xml");
        addMimeType("bmp", "application/x-bmp");
        addMimeType("bot", "application/x-bot");
        addMimeType("c4t", "application/x-c4t");
        addMimeType("c90", "application/x-c90");
        addMimeType("cal", "application/x-cals");
        addMimeType("cat", "application/vnd.ms-pki.seccat");
        addMimeType("cdf", "application/x-netcdf");
        addMimeType("cdr", "application/x-cdr");
        addMimeType("cel", "application/x-cel");
        addMimeType("cer", "application/x-x509-ca-cert");
        addMimeType("cg4", "application/x-g4");
        addMimeType("cgm", "application/x-cgm");
        addMimeType("cit", "application/x-cit");
        addMimeType("class", "java/*");
        addMimeType("cml", "text/xml");
        addMimeType("cmp", "application/x-cmp");
        addMimeType("cmx", "application/x-cmx");
        addMimeType("cot", "application/x-cot");
        addMimeType("crl", "application/pkix-crl");
        addMimeType("crt", "application/x-x509-ca-cert");
        addMimeType("csi", "application/x-csi");
        addMimeType("css", "text/css");
        addMimeType("cut", "application/x-cut");
        addMimeType("dbf", "application/x-dbf");
        addMimeType("dbm", "application/x-dbm");
        addMimeType("dbx", "application/x-dbx");
        addMimeType("dcd", "text/xml");
        addMimeType("dcx", "application/x-dcx");
        addMimeType("der", "application/x-x509-ca-cert");
        addMimeType("dgn", "application/x-dgn");
        addMimeType("dib", "application/x-dib");
        addMimeType("dll", "application/x-msdownload");
        addMimeType("doc", "application/msword");
        addMimeType("dot", "application/msword");
        addMimeType("drw", "application/x-drw");
        addMimeType("dtd", "text/xml");
        addMimeType("dwf", "Model/vnd.dwf");
        addMimeType("dwf", "application/x-dwf");
        addMimeType("dwg", "application/x-dwg");
        addMimeType("dxb", "application/x-dxb");
        addMimeType("dxf", "application/x-dxf");
        addMimeType("edn", "application/vnd.adobe.edn");
        addMimeType("emf", "application/x-emf");
        addMimeType("eml", "message/rfc822");
        addMimeType("ent", "text/xml");
        addMimeType("epi", "application/x-epi");
        addMimeType("eps", "application/x-ps");
        addMimeType("eps", "application/postscript");
        addMimeType("etd", "application/x-ebx");
        addMimeType("exe", "application/x-msdownload");
        addMimeType("fax", "image/fax");
        addMimeType("fdf", "application/vnd.fdf");
        addMimeType("fif", "application/fractals");
        addMimeType("fo", "text/xml");
        addMimeType("frm", "application/x-frm");
        addMimeType("g4", "application/x-g4");
        addMimeType("gbr", "application/x-gbr");
        addMimeType("gif", "image/gif");
        addMimeType("gl2", "application/x-gl2");
        addMimeType("gp4", "application/x-gp4");
        addMimeType("hgl", "application/x-hgl");
        addMimeType("hmr", "application/x-hmr");
        addMimeType("hpg", "application/x-hpgl");
        addMimeType("hpl", "application/x-hpl");
        addMimeType("hqx", "application/mac-binhex40");
        addMimeType("hrf", "application/x-hrf");
        addMimeType("hta", "application/hta");
        addMimeType("htc", "text/x-component");
        addMimeType("html", "text/html");
        addMimeType("htm", "text/html");
        addMimeType("htt", "text/webviewhtml");
        addMimeType("htx", "text/html");
        addMimeType("icb", "application/x-icb");
        addMimeType("ico", "image/x-icon");
        addMimeType("ico", "application/x-ico");
        addMimeType("iff", "application/x-iff");
        addMimeType("ig4", "application/x-g4");
        addMimeType("igs", "application/x-igs");
        addMimeType("iii", "application/x-iphone");
        addMimeType("img", "application/x-img");
        addMimeType("ins", "application/x-internet-signup");
        addMimeType("ipa", "application/vnd.iphone");
        addMimeType("isp", "application/x-internet-signup");
        addMimeType("IVF", "video/x-ivf");
        addMimeType("java", "java/*");
        addMimeType("jpg", "image/jpeg");
        addMimeType("jfif", "image/jpeg");
        addMimeType("jpe", "image/jpeg");
        addMimeType("jpe", "application/x-jpe");
        addMimeType("jpeg", "image/jpeg");
        addMimeType("jpg", "application/x-jpg");
        addMimeType("js", "application/x-javascript");
        addMimeType("jsp", "text/html");
        addMimeType("la1", "audio/x-liquid-file");
        addMimeType("lar", "application/x-laplayer-reg");
        addMimeType("latex", "application/x-latex");
        addMimeType("lavs", "audio/x-liquid-secure");
        addMimeType("lbm", "application/x-lbm");
        addMimeType("lmsff", "audio/x-la-lms");
        addMimeType("ls", "application/x-javascript");
        addMimeType("ltr", "application/x-ltr");
        addMimeType("m1v", "video/x-mpeg");
        addMimeType("m2v", "video/x-mpeg");
        addMimeType("m3u", "audio/mpegurl");
        addMimeType("m4e", "video/mpeg4");
        addMimeType("mac", "application/x-mac");
        addMimeType("man", "application/x-troff-man");
        addMimeType("math", "text/xml");
        addMimeType("mdb", "application/msaccess");
        addMimeType("mdb", "application/x-mdb");
        addMimeType("mfp", "application/x-shockwave-flash");
        addMimeType("mht", "message/rfc822");
        addMimeType("mhtml", "message/rfc822");
        addMimeType("mi", "application/x-mi");
        addMimeType("mid", "audio/mid");
        addMimeType("midi", "audio/mid");
        addMimeType("mil", "application/x-mil");
        addMimeType("mml", "text/xml");
        addMimeType("mnd", "audio/x-musicnet-download");
        addMimeType("mns", "audio/x-musicnet-stream");
        addMimeType("mocha", "application/x-javascript");
        addMimeType("movie", "video/x-sgi-movie");
        addMimeType("mp1", "audio/mp1");
        addMimeType("mp2", "audio/mp2");
        addMimeType("mp2v", "video/mpeg");
        addMimeType("mp3", "audio/mp3");
        addMimeType("mp4", "video/mpeg4");
        addMimeType("mpa", "video/x-mpg");
        addMimeType("mpd", "application/vnd.ms-project");
        addMimeType("mpe", "video/x-mpeg");
        addMimeType("mpeg", "video/mpg");
        addMimeType("mpg", "video/mpg");
        addMimeType("mpga", "audio/rn-mpeg");
        addMimeType("mpp", "application/vnd.ms-project");
        addMimeType("mps", "video/x-mpeg");
        addMimeType("mpt", "application/vnd.ms-project");
        addMimeType("mpv", "video/mpg");
        addMimeType("mpv2", "video/mpeg");
        addMimeType("mpw", "application/vnd.ms-project");
        addMimeType("mpx", "application/vnd.ms-project");
        addMimeType("mtx", "text/xml");
        addMimeType("mxp", "application/x-mmxp");
        addMimeType("net", "image/pnetvue");
        addMimeType("nrf", "application/x-nrf");
        addMimeType("nws", "message/rfc822");
        addMimeType("odc", "text/x-ms-odc");
        addMimeType("out", "application/x-out");
        addMimeType("p10", "application/pkcs10");
        addMimeType("p12", "application/x-pkcs12");
        addMimeType("p7b", "application/x-pkcs7-certificates");
        addMimeType("p7c", "application/pkcs7-mime");
        addMimeType("p7m", "application/pkcs7-mime");
        addMimeType("p7r", "application/x-pkcs7-certreqresp");
        addMimeType("p7s", "application/pkcs7-signature");
        addMimeType("pc5", "application/x-pc5");
        addMimeType("pci", "application/x-pci");
        addMimeType("pcl", "application/x-pcl");
        addMimeType("pcx", "application/x-pcx");
        addMimeType("pdf", "application/pdf");
        addMimeType("pdx", "application/vnd.adobe.pdx");
        addMimeType("pfx", "application/x-pkcs12");
        addMimeType("pgl", "application/x-pgl");
        addMimeType("pic", "application/x-pic");
        addMimeType("pko", "application/vnd.ms-pki.pko");
        addMimeType("pl", "application/x-perl");
        addMimeType("plg", "text/html");
        addMimeType("pls", "audio/scpls");
        addMimeType("plt", "application/x-plt");
        addMimeType("png", "image/png");
        addMimeType("png", "application/x-png");
        addMimeType("pot", "application/vnd.ms-powerpoint");
        addMimeType("ppa", "application/vnd.ms-powerpoint");
        addMimeType("ppm", "application/x-ppm");
        addMimeType("pps", "application/vnd.ms-powerpoint");
        addMimeType("ppt", "application/vnd.ms-powerpoint");
        addMimeType("ppt", "application/x-ppt");
        addMimeType("pr", "application/x-pr");
        addMimeType("prf", "application/pics-rules");
        addMimeType("prn", "application/x-prn");
        addMimeType("prt", "application/x-prt");
        addMimeType("ps", "application/x-ps");
        addMimeType("ps", "application/postscript");
        addMimeType("ptn", "application/x-ptn");
        addMimeType("pwz", "application/vnd.ms-powerpoint");
        addMimeType("r3t", "text/vnd.rn-realtext3d");
        addMimeType("ra", "audio/vnd.rn-realaudio");
        addMimeType("ram", "audio/x-pn-realaudio");
        addMimeType("ras", "application/x-ras");
        addMimeType("rat", "application/rat-file");
        addMimeType("rdf", "text/xml");
        addMimeType("rec", "application/vnd.rn-recording");
        addMimeType("red", "application/x-red");
        addMimeType("rgb", "application/x-rgb");
        addMimeType("rjs", "application/vnd.rn-realsystem-rjs");
        addMimeType("rjt", "application/vnd.rn-realsystem-rjt");
        addMimeType("rlc", "application/x-rlc");
        addMimeType("rle", "application/x-rle");
        addMimeType("rm", "application/vnd.rn-realmedia");
        addMimeType("rmf", "application/vnd.adobe.rmf");
        addMimeType("rmi", "audio/mid");
        addMimeType("rmj", "application/vnd.rn-realsystem-rmj");
        addMimeType("rmm", "audio/x-pn-realaudio");
        addMimeType("rmp", "application/vnd.rn-rn_music_package");
        addMimeType("rms", "application/vnd.rn-realmedia-secure");
        addMimeType("rmvb", "application/vnd.rn-realmedia-vbr");
        addMimeType("rmx", "application/vnd.rn-realsystem-rmx");
        addMimeType("rnx", "application/vnd.rn-realplayer");
        addMimeType("rp", "image/vnd.rn-realpix");
        addMimeType("rpm", "audio/x-pn-realaudio-plugin");
        addMimeType("rsml", "application/vnd.rn-rsml");
        addMimeType("rt", "text/vnd.rn-realtext");
        addMimeType("rtf", "application/msword");
        addMimeType("rtf", "application/x-rtf");
        addMimeType("rv", "video/vnd.rn-realvideo");
        addMimeType("sam", "application/x-sam");
        addMimeType("sat", "application/x-sat");
        addMimeType("sdp", "application/sdp");
        addMimeType("sdw", "application/x-sdw");
        addMimeType("sis", "application/vnd.symbian.install");
        addMimeType("sisx", "application/vnd.symbian.install");
        addMimeType("sit", "application/x-stuffit");
        addMimeType("slb", "application/x-slb");
        addMimeType("sld", "application/x-sld");
        addMimeType("slk", "drawing/x-slk");
        addMimeType("smi", "application/smil");
        addMimeType("smil", "application/smil");
        addMimeType("smk", "application/x-smk");
        addMimeType("snd", "audio/basic");
        addMimeType("sol", "text/plain");
        addMimeType("sor", "text/plain");
        addMimeType("spc", "application/x-pkcs7-certificates");
        addMimeType("spl", "application/futuresplash");
        addMimeType("spp", "text/xml");
        addMimeType("ssm", "application/streamingmedia");
        addMimeType("sst", "application/vnd.ms-pki.certstore");
        addMimeType("stl", "application/vnd.ms-pki.stl");
        addMimeType("stm", "text/html");
        addMimeType("sty", "application/x-sty");
        addMimeType("svg", "text/xml");
        addMimeType("swf", "application/x-shockwave-flash");
        addMimeType("tdf", "application/x-tdf");
        addMimeType("tg4", "application/x-tg4");
        addMimeType("tga", "application/x-tga");
        addMimeType("tif", "image/tiff");
        addMimeType("tif", "application/x-tif");
        addMimeType("tiff", "image/tiff");
        addMimeType("tld", "text/xml");
        addMimeType("top", "drawing/x-top");
        addMimeType("torrent", "application/x-bittorrent");
        addMimeType("tsd", "text/xml");
        addMimeType("txt", "text/plain");
        addMimeType("uin", "application/x-icq");
        addMimeType("uls", "text/iuls");
        addMimeType("vcf", "text/x-vcard");
        addMimeType("vda", "application/x-vda");
        addMimeType("vdx", "application/vnd.visio");
        addMimeType("vml", "text/xml");
        addMimeType("vpg", "application/x-vpeg005");
        addMimeType("vsd", "application/vnd.visio");
        addMimeType("vsd", "application/x-vsd");
        addMimeType("vss", "application/vnd.visio");
        addMimeType("vst", "application/vnd.visio");
        addMimeType("vst", "application/x-vst");
        addMimeType("vsw", "application/vnd.visio");
        addMimeType("vsx", "application/vnd.visio");
        addMimeType("vtx", "application/vnd.visio");
        addMimeType("vxml", "text/xml");
        addMimeType("wav", "audio/wav");
        addMimeType("wax", "audio/x-ms-wax");
        addMimeType("wb1", "application/x-wb1");
        addMimeType("wb2", "application/x-wb2");
        addMimeType("wb3", "application/x-wb3");
        addMimeType("wbmp", "image/vnd.wap.wbmp");
        addMimeType("wiz", "application/msword");
        addMimeType("wk3", "application/x-wk3");
        addMimeType("wk4", "application/x-wk4");
        addMimeType("wkq", "application/x-wkq");
        addMimeType("wks", "application/x-wks");
        addMimeType("wm", "video/x-ms-wm");
        addMimeType("wma", "audio/x-ms-wma");
        addMimeType("wmd", "application/x-ms-wmd");
        addMimeType("wmf", "application/x-wmf");
        addMimeType("wml", "text/vnd.wap.wml");
        addMimeType("wmv", "video/x-ms-wmv");
        addMimeType("wmx", "video/x-ms-wmx");
        addMimeType("wmz", "application/x-ms-wmz");
        addMimeType("wp6", "application/x-wp6");
        addMimeType("wpd", "application/x-wpd");
        addMimeType("wpg", "application/x-wpg");
        addMimeType("wpl", "application/vnd.ms-wpl");
        addMimeType("wq1", "application/x-wq1");
        addMimeType("wr1", "application/x-wr1");
        addMimeType("wri", "application/x-wri");
        addMimeType("wrk", "application/x-wrk");
        addMimeType("ws", "application/x-ws");
        addMimeType("ws2", "application/x-ws");
        addMimeType("wsc", "text/scriptlet");
        addMimeType("wsdl", "text/xml");
        addMimeType("wvx", "video/x-ms-wvx");
        addMimeType("x_b", "application/x-x_b");
        addMimeType("x_t", "application/x-x_t");
        addMimeType("xap", "application/x-silverlight-app");
        addMimeType("xdp", "application/vnd.adobe.xdp");
        addMimeType("xdr", "text/xml");
        addMimeType("xfd", "application/vnd.adobe.xfd");
        addMimeType("xfdf", "application/vnd.adobe.xfdf");
        addMimeType("xhtml", "text/html");
        addMimeType("xls", "application/vnd.ms-excel");
        addMimeType("xls", "application/x-xls");
        addMimeType("xlw", "application/x-xlw");
        addMimeType("xpl", "audio/scpls");
        addMimeType("xq", "text/xml");
        addMimeType("xql", "text/xml");
        addMimeType("xquery", "text/xml");
        addMimeType("xsd", "text/xml");
        addMimeType("xsl", "text/xml");
        addMimeType("xslt", "text/xml");
        addMimeType("xwd", "application/x-xwd");

    }

    public static void addMimeType(String extension, String mime) {
        MimeType m = new MimeType(mime, extension);
        MIME_MAP.computeIfAbsent(extension, (ext) -> new LinkedHashSet<>()).add(m);
        TYPE_MAP.computeIfAbsent(mime, (mm) -> new LinkedHashSet<>()).add(m);
    }

    private String extension = "";
    private String mime      = "";

    /**
     * Instantiates a new Mime type.
     */
    public MimeType() {
    }

    /**
     * Instantiates a new Mime type.
     *
     * @param mime      the mime
     * @param extension the extension
     */
    public MimeType(String mime, String extension) {
        this.extension = extension;
        this.mime = mime;
    }

    /**
     * Gets extension.
     *
     * @return the extension
     */
    public String getExtension() {
        return extension;
    }

    /**
     * Sets extension.
     *
     * @param extension the extension
     */
    public void setExtension(String extension) {
        this.extension = extension;
    }

    /**
     * Gets mime.
     *
     * @return the mime
     */
    public String getMime() {
        return mime;
    }

    /**
     * Sets mime.
     *
     * @param mime the mime
     */
    public void setMime(String mime) {
        this.mime = mime;
    }

    @Override
    public int hashCode() {
        return extension.hashCode() ^ mime.hashCode();
    }

    @Override
    public boolean equals(Object obj) {
        return obj instanceof MimeType && extension.equals(((MimeType) obj).extension) && mime.equals(((MimeType) obj).mime);
    }

    /**
     * Gets mime by filename.
     *
     * @param filename the filename
     * @return the mime by filename
     */
    @NotNull
    public static Collection<MimeType> getMimeTypesByFilename(String filename) {
        Set<MimeType> types = Collections.emptySet();
        if (filename != null) {
            String ext = FileUtils.getFileSuffix(filename);
            if (ext != null) {
                types = MIME_MAP.get(ext);
            }
        }
        if (types == null) {
            types = MIME_MAP.get("*");
        }
        return types;
    }

    /**
     * Gets mime by filename.
     *
     * @param filename the filename
     * @return the mime by filename
     */
    public static MimeType getMimeTypeByFilename(String filename) {
        Collection<MimeType> types = getMimeTypesByFilename(filename);
        return types.isEmpty() ? null : types.iterator().next();
    }

    /**
     * Gets mime by extension.
     *
     * @param extension the extension
     * @return the mime by extension
     */
    public static Collection<MimeType> getMimeTypesByExtension(String extension) {
        Set<MimeType> types = Collections.emptySet();
        if (StringUtils.isNotBlank(extension)) {
            types = MIME_MAP.get(extension);
        }
        if (types == null) {
            types = MIME_MAP.get("*");
        }
        return types;
    }

    /**
     * Gets mime by extension.
     *
     * @param extension the extension
     * @return the mime by extension
     */
    public static MimeType getMimeTypeByExtension(String extension) {
        Collection<MimeType> types = getMimeTypesByExtension(extension);
        return types.isEmpty() ? null : types.iterator().next();
    }

    /**
     * Gets mime types by mime.
     *
     * @param mime the mime
     * @return the mime types by mime
     */
    public static Collection<MimeType> getMimeTypesByMime(String mime) {
        return TYPE_MAP.getOrDefault(mime, Collections.emptySet());
    }

    /**
     * Gets mime type by mime.
     *
     * @param mime the mime
     * @return the mime type by mime
     */
    public static MimeType getMimeTypeByMime(String mime) {
        Set<MimeType> types = TYPE_MAP.get(mime);
        if (types != null && types.size() > 0) {
            return types.iterator().next();
        } else {
            return null;
        }
    }

    public static boolean matchContent(HttpServletRequest request, ContentType contentType) {
        return matchContent(request, contentType.getMimeType());
    }

    public static boolean matchContent(HttpServletRequest request, MimeType mimeType) {
        return matchContent(request, mimeType.getMime());
    }

    public static boolean matchContent(HttpServletRequest request, String mimeType) {
        // Check the content type to make sure it's contentType
        // Access header two ways to work around WebSphere oddities
        String type  = null;
        String type1 = request.getHeader("Content-Type");
        String type2 = request.getContentType();
        if (type1 == null && type2 != null) {
            // If one value is null, choose the other value
            type = type2;
        } else if (type2 == null && type1 != null) {
            type = type1;
        } else if (type1 != null) {
            // If neither value is null, choose the longer value
            type = (type1.length() > type2.length() ? type1 : type2);
        }

        return type != null && type.toLowerCase().startsWith(mimeType);
    }

}
